﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using YAPL.Language.Core.Exceptions;

namespace YAPL.Language.Core.Types {
	/// <summary>
	/// Base class for all YAPL types.
	/// YAPL types are divided into 2 cathergories:
	/// 1) Elementary types (such as YAPLInt, YAPLString, etc.)
	/// 2) Classes (built-in classes and user-defined classes either).
	/// Elementary types are iherited directly from YAPLObject,
	/// classes are inherited from YAPLClass.
	/// </summary>
	public class YAPLObject {
		protected string _className;
		protected Scope _instanceVariables = new Scope();

		private static readonly YAPLObject _undefined = new YAPLObject("undefined");
		private static readonly YAPLObject _globalObject = new YAPLObject(ClassNames.GlobalScope);

		public static YAPLObject CreateFromObject(Type yaplType, object value) {
			if (yaplType == typeof(YAPLString)) {
				return new YAPLString((string) value);
			}
			if (yaplType == typeof(YAPLInt)) {
				return new YAPLInt((int)value);
			}
			if (yaplType == typeof(YAPLFloat)) {
				return new YAPLFloat((double)value);
			}
			if (yaplType == typeof(YAPLBoolean)) {
				return new YAPLBoolean((bool)value);
			}
			return UNDEFINED;
		}

		public YAPLObject(string className) {
			_className = className;
		}

		public YAPLObject() : this(ClassNames.YAPLObject) { }

		public override string ToString() {
			if (ReferenceEquals(this, _undefined))
				return "undefined";
			return typeof(YAPLObject).ToString();
		}

		public virtual YAPLString ToYAPLString() {
			return new YAPLString(ToString());
		}

		public virtual void CreateDynamicClass() {
			if (!YAPLType.IsDynamicClass()) {
				var dest = _className +"_" + YAPLClass.GetUniqueClassId() + YAPLClass.DYNAMIC_CLASS_SUFFIX;
				GCL.CloneClass(_className, dest);
				_className = dest;
			}
		}

		public string YAPLTypeName {
			get {
				if (_className != null) {
					return _className;
				}
				var typeStr = ReferenceEquals(this, _undefined) ? "undefined" : GetType().ToString();
				return typeStr.Substring(typeStr.LastIndexOf('.') + 1);
			}
		}

		public Scope InstanceVariables {
			get { return _instanceVariables; }
		}

		public static YAPLObject UNDEFINED {
			get { return _undefined; }
		}

		public static YAPLObject GLOBAL_OBJECT {
			get { return _globalObject; }
		}

		public virtual YAPLClass YAPLType {
			get {
				if (_className != null)
					return GCL.Get(_className);
				//TODO add YAPLClass reference to objects with undefined class name
				return null;
			}
		}

		public virtual object Value {
			get {
				//TODO put something more meaningful here
				return null;
			}
		}
		#region operator virtual functions

		public virtual YAPLObject Add(YAPLObject other) {
			throw new YAPLException("'+' Operator can't be applied to the following objects: " + YAPLTypeName + ", " +
															other.YAPLTypeName);
		}

		public virtual YAPLObject Subtract(YAPLObject other) {
			throw new YAPLException("'-' Operator can't be applied to the following object: " + YAPLTypeName + ", " +
															other.YAPLTypeName);
		}

		public virtual YAPLObject Multiply(YAPLObject other) {
			throw new YAPLException("'*' Operator can't be applied to the following object: " + YAPLTypeName + ", " +
															other.YAPLTypeName);
		}

		public virtual YAPLObject Divide(YAPLObject other) {
			throw new YAPLException("'/' Operator can't be applied to the following object: " + YAPLTypeName + ", " +
															other.YAPLTypeName);
		}

		public virtual YAPLObject Mod(YAPLObject other) {
			throw new YAPLException("'%' Operator can't be applied to the following object: " + YAPLTypeName + ", " +
															other.YAPLTypeName);
		}

		public virtual YAPLObject UnaryMinus() {
			throw new YAPLException("Unary '-' Operator can't be applied to the following object: " + YAPLTypeName);
		}

		public virtual YAPLObject UnaryPlus() {
			throw new YAPLException("Unary '+' Operator can't be applied to the following object: " + YAPLTypeName);
		}

		public virtual YAPLObject Increment() {
			throw new YAPLException("'++' Operator can't be applied to the following object: " + YAPLTypeName);
		}

		public virtual YAPLObject Decrement() {
			throw new YAPLException("'--' Operator can't be applied to the following object: " + YAPLTypeName);
		}

		public virtual YAPLObject And(YAPLObject other) {
			throw new YAPLException("'&&' Operator can't be applied to the following object: " + YAPLTypeName);
		}

		public virtual YAPLObject Or(YAPLObject other) {
			throw new YAPLException("'||' Operator can't be applied to the following object: " + YAPLTypeName);
		}

		public virtual YAPLObject Not() {
			throw new YAPLException("'!' Operator can't be applied to the following object: " + YAPLTypeName);
		}
		#endregion



	}
}